package cn.yangliu.task.config;

import cn.yangliu.task.handler.TaskPublisher;
import cn.yangliu.task.handler.TaskSubscription;
import cn.yangliu.task.handler.TaskUpdateHandler;
import cn.yangliu.task.lock.Locker;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPool;

@Configuration
@EnableConfigurationProperties(value = TaskClusterProperties.class)
@ConditionalOnProperty(prefix = "spring.task.cluster", value = "enable", matchIfMissing = true)
public class TaskClusterAutoConfig {

    @Autowired
    private TaskClusterProperties properties;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private RedisSerializer<String> redisSerializer = new StringRedisSerializer();

    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setValueSerializer(redisSerializer);
        redisTemplate.setStringSerializer(redisSerializer);
        redisTemplate.setDefaultSerializer(redisSerializer);
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setTopicSerializer(redisSerializer);
        container.setConnectionFactory(connectionFactory);

        container.addMessageListener(taskSubscription(), new PatternTopic(TaskPublisher.PUBLISH_TYPE_CANCLE));
        container.addMessageListener(taskSubscription(), new PatternTopic(TaskPublisher.PUBLISH_TYPE_CREATE));
        container.addMessageListener(taskSubscription(), new PatternTopic(TaskPublisher.PUBLISH_TYPE_RESTART));
        return container;
    }

    @Bean
    @Primary
    public Locker redisLocker() {
        try {
            Class<? extends Locker> clazz = properties.getLockerImplClass();
            return clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    @Value("${spring.redis.database:0}")
    private int database = 0;
    @Value("${spring.redis.host:127.0.0.1}")
    private String host;
    @Value("${spring.redis.password:}")
    private String password;
    @Value("${spring.redis.port:6379}")
    private int port;
    @Value("${spring.redis.timeout:2000}")
    private int timeout;

    @ConditionalOnMissingBean
    @Bean
    public JedisPool jedisPool() {
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxIdle(30);
        config.setMinIdle(10);
        config.setMaxTotal(50);
        password = "".equals(password) ? null : password;
        return new JedisPool(config, host, port, timeout, password, database, null);
    }

    @Bean
    @Primary
    public TaskUpdateHandler taskUpdateHandlerImpl() {
        try {
            Class<? extends TaskUpdateHandler> clazz = properties.getTaskUpdateHandlerClass();
            return clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Bean
    @ConditionalOnMissingBean
    public TaskSubscription taskSubscription() {
        return new TaskSubscription();
    }

    @Bean
    @ConditionalOnMissingBean
    public TaskPublisher taskPublisher() {
        return new TaskPublisher();
    }

}
